16-1 nestjs用户认证:创建认证控制器及对接数据
课程目标与接口设计
功能需求分析
1. 核心功能实现
- 用户注册:实现新用户账号创建功能
- 用户登录:实现已有用户身份验证功能
- 安全要求:
- 密码必须加密存储
- 敏感信息不得明文传输
- 实现基本的防暴力破解机制
2. RESTful接口规范
// 认证接口
POST /auth/register // 用户注册
POST /auth/login // 用户登录
// 响应格式
{
"code": 200,
"data": {
"token": "xxxxx",
"userInfo": {...}
},
"message": "success"
}
typescript
💡提示:遵循HTTP状态码规范(200成功,400客户端错误,500服务端错误)
3. 接口安全设计
- HTTPS加密传输
- 密码字段前端加密(如使用RSA)
- 接口限流(如1分钟5次尝试)
- 敏感操作日志记录
接口测试方案
1. 测试环境配置
// Postman环境变量配置
{
"base_url": "http://localhost:3000/api/v1",
"test_username": "testuser",
"test_password": "P@ssw0rd123!"
}
javascript
2. 测试用例设计
测试场景 | 请求方法 | 预期结果 | 测试数据 |
---|---|---|---|
正常注册 | POST /register | 201 Created | 合法用户名+强密码 |
重复注册 | POST /register | 409 Conflict | 已存在用户名 |
弱密码注册 | POST /register | 400 Bad Request | 密码强度不足 |
正常登录 | POST /login | 200 OK | 正确凭证 |
错误密码 | POST /login | 401 Unauthorized | 错误密码 |
3. 自动化测试脚本
// 使用Jest的测试示例
test('用户注册成功', async () => {
const response = await request(app)
.post('/auth/register')
.send({
username: 'newuser',
password: 'Str0ngP@ss'
});
expect(response.statusCode).toBe(201);
expect(response.body).toHaveProperty('token');
});
javascript
扩展知识
1. OAuth2.0集成
2. JWT令牌设计
// 令牌payload示例
interface JwtPayload {
sub: string; // 用户ID
iat: number; // 签发时间
exp: number; // 过期时间
role: string; // 用户角色
}
typescript
3. 性能优化建议
- 使用Redis缓存用户信息
- 实现令牌黑名单机制
- 数据库查询添加索引
- 启用HTTP/2协议
常见问题解答
Q:为什么要将登录注册单独设计为/auth路径? A:符合安全最佳实践,隔离认证相关接口,便于统一实施安全策略(如限流、监控)
Q:测试时出现跨域问题怎么办? A:确保后端已配置CORS:
// NestJS CORS配置
app.enableCors({
origin: ['http://localhost:8080'],
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE'
});
typescript
Q:如何模拟并发注册测试? A:使用JMeter或LoadRunner工具,配置线程组模拟多用户同时注册
延伸学习资源
模块化设计优势
架构分离原则
1. 模块化核心价值
- 功能解耦:认证逻辑与用户管理完全分离
- 独立演进:各模块可独立升级不影响其他功能
- 安全边界:敏感操作集中在Auth模块统一管控
- 团队协作:不同团队可并行开发不同模块
2. 具体分离方案
3. 设计模式应用
- 策略模式:认证方式可替换(如密码/短信/第三方登录)
- 门面模式:Auth模块对外提供统一认证接口
- 观察者模式:用户状态变更通知认证系统
💡提示:遵循"高内聚低耦合"设计原则,模块间通过明确定义的接口通信
跨模块协作
1. 依赖管理实现
// 用户模块配置
@Module({
providers: [
{
provide: 'USER_REPO',
useClass: UserRepository
}
],
exports: ['USER_REPO'] // 明确导出符号
})
// 认证模块配置
@Module({
imports: [UserModule],
providers: [
{
provide: 'AUTH_SERVICE',
inject: ['USER_REPO'], // 声明依赖
useFactory: (repo) => new AuthService(repo)
}
]
})
typescript
2. 循环依赖解决方案
// 使用forwardRef处理循环引用
@Module({
imports: [forwardRef(() => UserModule)]
})
typescript
3. 性能优化技巧
- 懒加载模块:对于非核心模块
@Module({
imports: [UserModule.forRoot({ lazy: true })]
})
typescript
- 共享实例:相同服务单例化
{
provide: 'SHARED_SERVICE',
useExisting: ExistingService // 重用实例
}
typescript
扩展实践案例
案例1:多因素认证扩展
// auth.module.ts
@Module({
providers: [
{
provide: 'AUTH_STRATEGIES',
useValue: [PasswordStrategy, SMSCodeStrategy, OAuthStrategy]
}
]
})
typescript
案例2:模块热替换
// 开发环境动态加载模块
if (process.env.NODE_ENV === 'development') {
import('./experimental-auth.module').then(m => {
app.select(AuthModule).replaceModule(m.ExperimentalAuthModule)
})
}
typescript
常见问题解答
Q:模块划分粒度过细怎么办? A:采用"领域驱动设计",按业务能力划分:
- 用户管理域(User/Profile/Role)
- 安全域(Auth/AccessControl/Audit)
- 业务域(Order/Payment/Inventory)
Q:如何监控模块间调用? A:使用NestJS拦截器:
@Injectable()
class ModuleCallLogger implements NestInterceptor {
intercept(context, next) {
console.log(`调用模块: ${context.getClass().name}`)
return next.handle()
}
}
typescript
Q:多模块共享配置如何处理? A:创建Config核心模块:
@Global() // 全局模块
@Module({
providers: [ConfigService],
exports: [ConfigService]
})
typescript
前沿技术动态
- 微前端集成:将Auth模块打包为独立微应用
- Serverless架构:各模块部署为独立云函数
- WASM加速:密码计算密集型操作用Rust实现
延伸学习资源
Auth模块创建
CLI生成命令详解
1. 命令功能解析
# 生成模块骨架(包含模块定义和NestJS装饰器)
nest g module auth
# 生成控制器(自动添加@Controller装饰器)
nest g controller auth --flat # --flat表示不创建子目录
# 生成服务层(自动添加@Injectable装饰器)
nest g service auth --spec # --spec表示生成测试文件
bash
2. 高级生成选项
参数 | 作用 | 示例 |
---|---|---|
--dry-run | 模拟执行不实际生成文件 | nest g module auth --dry-run |
--project | 指定多项目工作区中的项目 | nest g module auth --project=admin |
--no-spec | 不生成测试文件 | nest g service auth --no-spec |
3. 目录结构优化建议
推荐采用功能模块分组:
src/
auth/
controllers/
auth.controller.ts
social.controller.ts # 第三方登录
services/
auth.service.ts
token.service.ts # JWT服务
strategies/
local.strategy.ts # 密码策略
entities/
user.entity.ts # 认证专用实体
auth.module.ts
text
模块配置文件深入
1. 完整模块配置示例
// auth.module.ts
@Module({
imports: [
UserModule,
JwtModule.registerAsync({ // 异步配置JWT
useFactory: () => ({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '1d' }
})
}),
ThrottlerModule.forRoot([{ // 限流配置
ttl: 60000,
limit: 5
}])
],
controllers: [AuthController, SocialController],
providers: [
AuthService,
{
provide: APP_GUARD,
useClass: ThrottlerGuard // 全局启用限流
}
],
exports: [AuthService] // 暴露认证服务
})
typescript
2. 动态模块配置
实现可配置的认证模块:
// auth.module.ts
export class AuthModule {
static forRoot(options: AuthOptions): DynamicModule {
return {
module: AuthModule,
providers: [
{
provide: 'AUTH_OPTIONS',
useValue: options
}
]
}
}
}
// 使用处
@Module({
imports: [AuthModule.forRoot({ tokenExpiresIn: '2h' })]
})
typescript
3. 模块生命周期钩子
@Module({
//...
})
export class AuthModule implements OnModuleInit {
onModuleInit() {
console.log('认证模块初始化完成')
}
}
typescript
最佳实践建议
- 分层清晰:
- Controller只处理HTTP相关逻辑
- Service实现核心业务规则
- Repository处理数据持久化
- 依赖注入规范:
// 推荐写法
constructor(
@InjectRepository(User)
private userRepository: Repository<User>,
@Inject('JWT_SERVICE')
private jwtService: JwtService
) {}
typescript
- 测试友好设计:
// auth.service.spec.ts
describe('AuthService', () => {
let service: AuthService;
let mockUserRepository: jest.Mocked<Repository<User>>;
beforeEach(async () => {
const module = await Test.createTestingModule({
providers: [
AuthService,
{
provide: getRepositoryToken(User),
useValue: mockRepository()
}
]
}).compile();
service = module.get<AuthService>(AuthService);
});
});
typescript
常见问题解决
Q:生成的测试文件报错? A:需要安装测试依赖:
npm install -D @nestjs/testing @types/jest
bash
Q:如何复用模块配置? A:创建共享配置模块:
// shared-config.module.ts
@Global()
@Module({
providers: [
{
provide: 'CONFIG',
useValue: loadConfig()
}
],
exports: ['CONFIG']
})
typescript
Q:模块循环依赖怎么处理? A:使用forwardRef:
// auth.module.ts
@Module({
imports: [forwardRef(() => UserModule)]
})
typescript
扩展应用场景
- 多租户支持:
@Module({
imports: [
TypeOrmModule.forFeature([User], 'tenant1_connection'),
TypeOrmModule.forFeature([User], 'tenant2_connection')
]
})
typescript
- GraphQL集成:
@Module({
imports: [GraphQLModule.forRoot({
autoSchemaFile: 'auth-schema.gql'
})]
})
typescript
- 微服务改造:
@Module({
imports: [
ClientsModule.register([
{
name: 'USER_SERVICE',
transport: Transport.TCP,
options: { port: 3001 }
}
])
]
})
typescript
性能优化技巧
- 延迟加载:
@Module({
imports: [UserModule.forRoot({ lazy: true })]
})
typescript
- 缓存策略:
@Module({
providers: [
{
provide: 'AUTH_CACHE',
useClass: CacheManager
}
]
})
typescript
- 请求管道优化:
@Module({
providers: [
{
provide: APP_PIPE,
useClass: FastifyValidationPipe // 比默认管道快3倍
}
]
})
typescript
通过以上扩展,Auth模块不仅能满足基础认证需求,还能适应各种复杂业务场景,同时保证代码的可维护性和性能表现。
控制器实现
路由处理方法详解
1. 完整控制器实现
@Controller('auth')
export class AuthController {
constructor(
private authService: AuthService,
private logger: Logger
) {}
@Post('register')
@HttpCode(201) // 明确返回状态码
@ApiOperation({ summary: '用户注册' }) // Swagger文档
async register(@Body() dto: RegisterDto) {
this.logger.log(`注册请求: ${JSON.stringify(dto)}`);
try {
const result = await this.authService.register(dto);
return {
code: 201,
data: result,
message: '注册成功'
};
} catch (error) {
this.logger.error('注册失败', error.stack);
throw new HttpException(
'注册失败',
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
@Post('login')
@UseGuards(LocalAuthGuard) // 使用本地策略守卫
@ApiResponse({ status: 200, type: LoginResponseDto })
async login(@Request() req) {
return this.authService.login(req.user);
}
}
typescript
2. 高级路由特性
- 版本控制:
@Controller({ path: 'auth', version: '1' })
typescript - 异步响应:
@Get('status') async checkStatus(): Promise<Observable<string>> { return from(['ok', 'healthy']).pipe(delay(1000)); }
typescript - 文件上传:
@Post('avatar') @UseInterceptors(FileInterceptor('file')) uploadAvatar(@UploadedFile() file: Express.Multer.File) { return this.authService.saveAvatar(file); }
typescript
3. 请求处理流程
DTO数据验证增强
1. 完整DTO定义
// register.dto.ts
export class RegisterDto {
@IsString()
@MinLength(3)
@MaxLength(20)
@Matches(/^[a-zA-Z0-9_]+$/, {
message: '用户名只能包含字母、数字和下划线'
})
username: string;
@IsStrongPassword({
minLength: 8,
minLowercase: 1,
minUppercase: 1,
minNumbers: 1,
minSymbols: 1
})
@Matches(/(?=.*[~!@#$%^&*()_+])/, {
message: '必须包含至少一个特殊字符'
})
password: string;
@IsEmail()
@MaxLength(50)
email: string;
@ValidateIf(o => o.phone)
@IsPhoneNumber('CN')
phone?: string;
}
typescript
2. 自定义验证器
// match-password.decorator.ts
export function MatchPassword(property: string) {
return (object: any, propertyName: string) => {
registerDecorator({
name: 'matchPassword',
target: object.constructor,
propertyName,
constraints: [property],
validator: {
validate(value: any, args: ValidationArguments) {
const [relatedPropertyName] = args.constraints;
const relatedValue = (args.object as any)[relatedPropertyName];
return value === relatedValue;
}
}
});
};
}
// 使用示例
export class RegisterDto {
@MatchPassword('passwordConfirm')
password: string;
}
typescript
3. 验证错误处理
// validation.filter.ts
@Catch(HttpException)
export class ValidationFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
const status = exception.getStatus();
if (status === HttpStatus.BAD_REQUEST) {
const res = exception.getResponse() as any;
return response.status(status).json({
code: status,
message: '验证失败',
errors: res.message.map(err => ({
field: err.property,
constraints: err.constraints
}))
});
}
//...其他错误处理
}
}
// 全局注册
app.useGlobalFilters(new ValidationFilter());
typescript
安全增强措施
- 防暴力破解:
@Post('login')
@Throttle(5, 60) // 每分钟最多5次尝试
async login(@Body() dto: LoginDto) {
//...
}
typescript
- 敏感字段过滤:
@Exclude()
export class UserDto {
@Expose()
username: string;
@Expose()
email: string;
password: string; // 不暴露
}
typescript
- CSRF防护:
@Post('logout')
@UseGuards(CsrfGuard)
async logout(@Headers('x-csrf-token') token: string) {
//...
}
typescript
测试用例示例
describe('AuthController', () => {
let controller: AuthController;
let mockAuthService: jest.Mocked<AuthService>;
beforeEach(async () => {
mockAuthService = {
register: jest.fn(),
login: jest.fn()
};
const module = await Test.createTestingModule({
controllers: [AuthController],
providers: [
{
provide: AuthService,
useValue: mockAuthService
}
]
}).compile();
controller = module.get<AuthController>(AuthController);
});
test('注册成功返回201', async () => {
mockAuthService.register.mockResolvedValue({ id: 1 });
const response = await controller.register(
new RegisterDto({ username: 'test', password: 'P@ssw0rd' })
);
expect(response.code).toBe(201);
});
});
typescript
扩展应用场景
- 多因素认证:
@Post('verify-2fa')
async verify2fa(@Body() dto: Verify2FADto) {
return this.authService.verify2FA(dto);
}
typescript
- 社交登录:
@Get('github')
@UseGuards(AuthGuard('github'))
async githubLogin() {}
@Get('github/callback')
@UseGuards(AuthGuard('github'))
async githubCallback(@Req() req) {
return this.authService.socialLogin(req.user);
}
typescript
- 设备授权:
@Post('device-auth')
async deviceAuth(
@Body() dto: DeviceAuthDto,
@Ip() clientIp: string
) {
return this.authService.deviceAuth(dto, clientIp);
}
typescript
通过以上扩展,控制器不仅能处理基础认证逻辑,还能满足企业级应用的安全需求和复杂业务场景。
服务层实现
核心处理流程详解
1. 完整认证流程架构
2. 密码安全增强方案
- 加密算法选择:
const saltRounds = 12; // 推荐值 const hashedPassword = await bcrypt.hash(password, saltRounds);
typescript - 密钥派生函数:
import * as crypto from 'crypto'; function deriveKey(password: string, salt: string) { return crypto.pbkdf2Sync( password, salt, 100000, // 迭代次数 64, // 密钥长度 'sha512' ).toString('hex'); }
typescript
3. 令牌生成策略
private generateTokens(user: User) {
const payload = {
sub: user.id,
roles: user.roles
};
return {
accessToken: this.jwtService.sign(payload, {
expiresIn: '15m',
secret: process.env.JWT_SECRET
}),
refreshToken: this.jwtService.sign(payload, {
expiresIn: '7d',
secret: process.env.JWT_REFRESH_SECRET
})
};
}
typescript
数据库操作增强
1. 事务处理实现
async registerWithTransaction(dto: RegisterDto) {
return this.dataSource.transaction(async manager => {
// 检查用户
const existingUser = await manager.findOne(User, {
where: { username: dto.username }
});
if (existingUser) {
throw new ConflictException('用户名已存在');
}
// 创建用户
const user = manager.create(User, {
username: dto.username,
password: await bcrypt.hash(dto.password, 12)
});
// 初始化用户配置
const profile = manager.create(Profile, {
userId: user.id,
status: 'active'
});
await manager.save([user, profile]);
return user;
});
}
typescript
2. 审计日志集成
async register(dto: RegisterDto, ipAddress: string) {
const user = await this.createUser(dto);
await this.auditLogRepository.save({
userId: user.id,
action: 'register',
ipAddress,
metadata: {
device: dto.deviceInfo
}
});
return user;
}
typescript
3. 缓存优化方案
async findUser(username: string) {
const cacheKey = `user:${username}`;
const cachedUser = await this.cacheManager.get<User>(cacheKey);
if (cachedUser) {
return cachedUser;
}
const user = await this.userRepository.findOne({
where: { username }
});
if (user) {
await this.cacheManager.set(cacheKey, user, { ttl: 300 });
}
return user;
}
typescript
异常处理体系
1. 自定义异常类型
export class AccountLockedException extends HttpException {
constructor() {
super('账户已锁定,请15分钟后再试', 423);
}
}
// 使用示例
if (loginAttempts > 5) {
throw new AccountLockedException();
}
typescript
2. 异常过滤器
@Catch(AccountLockedException)
export class AccountLockedFilter implements ExceptionFilter {
catch(exception: AccountLockedException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse();
response.status(exception.getStatus()).json({
code: 423,
message: exception.message,
retryAfter: 900 // 15分钟
});
}
}
typescript
安全增强措施
1. 登录尝试限制
async login(dto: LoginDto, ip: string) {
const attemptKey = `login:attempts:${ip}`;
const attempts = await this.cacheManager.get<number>(attemptKey) || 0;
if (attempts >= 5) {
throw new AccountLockedException();
}
try {
// 验证逻辑...
await this.cacheManager.del(attemptKey);
} catch (error) {
await this.cacheManager.set(attemptKey, attempts + 1, { ttl: 900 });
throw error;
}
}
typescript
2. 密码策略服务
@Injectable()
export class PasswordPolicyService {
private readonly historySize = 5;
async validatePasswordHistory(userId: string, newPassword: string) {
const history = await this.passwordHistoryRepository.find({
where: { userId },
order: { createdAt: 'DESC' },
take: this.historySize
});
return !history.some(record =>
bcrypt.compareSync(newPassword, record.hashedPassword)
);
}
}
typescript
测试策略示例
1. 单元测试
describe('AuthService', () => {
let service: AuthService;
let mockUserRepository: jest.Mocked<Repository<User>>;
beforeEach(async () => {
mockUserRepository = {
findOne: jest.fn(),
create: jest.fn(),
save: jest.fn()
};
const module = await Test.createTestingModule({
providers: [
AuthService,
{ provide: getRepositoryToken(User), useValue: mockUserRepository },
{ provide: 'bcrypt', useValue: { hash: jest.fn() } }
]
}).compile();
service = module.get<AuthService>(AuthService);
});
test('重复注册抛出冲突异常', async () => {
mockUserRepository.findOne.mockResolvedValue({} as User);
await expect(service.register(new RegisterDto())).rejects.toThrow(ConflictException);
});
});
typescript
2. 集成测试
describe('AuthService Integration', () => {
let app: INestApplication;
let authService: AuthService;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = module.createNestApplication();
await app.init();
authService = module.get<AuthService>(AuthService);
});
test('完整注册流程', async () => {
const dto = new RegisterDto();
dto.username = 'testuser';
dto.password = 'P@ssw0rd123!';
const result = await authService.register(dto);
expect(result).toHaveProperty('id');
expect(result.username).toBe(dto.username);
});
});
typescript
扩展功能实现
1. 多因素认证
async enable2FA(userId: string) {
const secret = authenticator.generateSecret();
await this.userRepository.update(userId, {
twoFactorSecret: secret
});
return authenticator.keyuri(
userId,
'MyApp',
secret
);
}
typescript
2. 社交账号关联
async linkSocialAccount(userId: string, provider: string, profile: any) {
return this.socialAccountRepository.upsert({
userId,
provider,
providerId: profile.id,
metadata: profile
}, ['userId', 'provider']);
}
typescript
3. 设备管理
async registerDevice(userId: string, deviceInfo: DeviceInfoDto) {
const fingerprint = this.deviceFingerprintService.generate(deviceInfo);
return this.deviceRepository.save({
userId,
fingerprint,
lastLogin: new Date(),
...deviceInfo
});
}
typescript
通过以上扩展实现,认证服务层不仅能处理基础的用户注册/登录功能,还能满足企业级应用在安全性、可扩展性和可维护性方面的需求。每个功能点都考虑了异常处理、性能优化和测试验证,确保系统稳定可靠。
跨模块数据访问
仓库模式实现详解
1. 完整仓库模式架构
2. 增强型仓库实现
// user.repository.ts
@EntityRepository(User)
export class UserRepository extends Repository<User> {
async findByUsername(username: string): Promise<User | undefined> {
return this.createQueryBuilder('user')
.where('user.username = :username', { username })
.addSelect('user.password') // 明确选择敏感字段
.getOne();
}
async createWithProfile(dto: CreateUserDto): Promise<User> {
const user = this.create(dto);
user.profile = new Profile(); // 自动创建关联配置
return this.save(user);
}
}
typescript
3. 模块导出优化方案
// user.module.ts
@Module({
imports: [TypeOrmModule.forFeature([User, Profile])],
providers: [
{
provide: 'USER_REPOSITORY',
useClass: UserRepository
}
],
exports: ['USER_REPOSITORY'] // 使用Token导出更安全
})
typescript
依赖注入高级实践
1. 多仓库注入方案
// auth.service.ts
constructor(
@Inject('USER_REPOSITORY')
private userRepository: UserRepository,
@InjectRepository(LoginLog)
private loginLogRepository: Repository<LoginLog>
) {}
typescript
2. 动态提供者注入
// auth.module.ts
@Module({
providers: [
{
provide: 'AUTH_REPOSITORY',
useFactory: (connection: Connection) => {
return connection.getCustomRepository(UserRepository);
},
inject: [Connection]
}
]
})
typescript
3. 接口抽象注入
// interfaces/user-repo.interface.ts
export interface IUserRepository {
findByUsername(username: string): Promise<User>;
}
// user.repository.ts
@Injectable()
export class UserRepository implements IUserRepository {
// 实现接口方法
}
// auth.service.ts
constructor(
@Inject('IUserRepository')
private userRepository: IUserRepository
) {}
typescript
性能优化技巧
1. 延迟加载关联数据
// user.repository.ts
async findWithProfile(username: string) {
return this.findOne({
where: { username },
relations: ['profile'], // 显式加载关联
loadEagerRelations: false // 禁用全局急切加载
});
}
typescript
2. 查询结果缓存
@Injectable()
export class UserRepository {
@Cacheable({ ttl: 300 })
async findById(id: string) {
return this.findOne({ where: { id } });
}
}
typescript
3. 批量操作优化
async updateLastLoginBatch(userIds: string[]) {
return this.createQueryBuilder()
.update(User)
.set({ lastLogin: new Date() })
.whereInIds(userIds)
.execute();
}
typescript
安全增强措施
1. 数据权限控制
@Injectable()
export class ScopedUserRepository {
constructor(
@InjectRepository(User)
private repository: Repository<User>,
@Inject(REQUEST) private request: Request
) {}
async findUser() {
return this.repository.findOne({
where: { tenantId: this.request.user.tenantId }
});
}
}
typescript
2. 敏感字段过滤
async findSafeUser(username: string) {
return this.createQueryBuilder()
.select([
'user.id',
'user.username',
'user.email' // 明确选择非敏感字段
])
.where('user.username = :username', { username })
.getOne();
}
typescript
测试策略
1. 仓库单元测试
describe('UserRepository', () => {
let repository: UserRepository;
let mockConnection: jest.Mocked<Connection>;
beforeEach(async () => {
mockConnection = {
getRepository: jest.fn()
} as any;
const module = await Test.createTestingModule({
providers: [
UserRepository,
{ provide: Connection, useValue: mockConnection }
]
}).compile();
repository = module.get<UserRepository>(UserRepository);
});
test('findByUsername应返回用户', async () => {
mockConnection.getRepository.mockReturnValue({
findOne: jest.fn().mockResolvedValue({ id: 1 })
});
const user = await repository.findByUsername('test');
expect(user).toHaveProperty('id');
});
});
typescript
2. 集成测试示例
describe('跨模块依赖测试', () => {
let app: INestApplication;
let authService: AuthService;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [UserModule, AuthModule],
}).compile();
app = module.createNestApplication();
await app.init();
authService = module.get<AuthService>(AuthService);
});
test('应能正确注入UserRepository', async () => {
await expect(
authService.register(new RegisterDto())
).resolves.not.toThrow();
});
});
typescript
扩展应用场景
1. 多租户数据隔离
@Injectable()
export class TenantUserRepository {
constructor(
@InjectRepository(User)
private repository: Repository<User>,
@Inject(TENANT_ID) private tenantId: string
) {}
async findUsers() {
return this.repository.find({
where: { tenantId: this.tenantId }
});
}
}
typescript
2. 读写分离实现
@Injectable()
export class UserReadRepository {
constructor(
@Inject('READ_CONNECTION')
private connection: Connection
) {}
async findUsers() {
return this.connection.getRepository(User).find();
}
}
typescript
3. 事件溯源模式
@Injectable()
export class EventSourcedUserRepository {
constructor(
private eventStore: EventStore
) {}
async save(user: User) {
const events = user.getUncommittedEvents();
await this.eventStore.append(events);
user.markEventsAsCommitted();
}
}
typescript
通过以上扩展,跨模块数据访问不仅实现了基本的依赖注入功能,还支持企业级应用在性能、安全和扩展性方面的各种需求。每个实现方案都包含对应的测试策略和实际应用场景,确保方案的可行性。
数据库连接验证
连接测试步骤详解
1. 容器化数据库部署
# 使用特定版本PostgreSQL
docker-compose -f docker-compose.db.yml up -d
# 验证容器状态
docker ps -a | grep postgres
# 查看数据库日志
docker logs <container_id>
bash
2. 数据库迁移与同步
# 开发环境增量迁移(生成迁移文件)
npx prisma migrate dev --name init
# 生产环境直接同步
npx prisma db push --accept-data-loss
# 回滚迁移
npx prisma migrate reset
bash
3. 高级调试技巧
# 带环境变量启动调试
POSTGRES_LOG_LEVEL=debug nest start --debug
# 监控Prisma引擎日志
PRISMA_LOG_QUERIES=true npm run dev
# 网络连通性测试
docker exec -it <container_id> psql -U postgres -c "\l"
bash
可视化工具集成
1. Prisma Studio
# 启动可视化编辑器
npx prisma studio --port 5555
# 浏览器访问
open http://localhost:5555
bash
2. 数据库管理工具
- TablePlus:支持SSH隧道连接
- DBeaver:企业级多数据库客户端
- pgAdmin:PostgreSQL官方工具
3. 监控面板配置
# docker-compose.monitor.yml
services:
pgweb:
image: sosedoff/pgweb
ports: ["8081:8081"]
environment:
DATABASE_URL: postgres://user:pass@db:5432/db?sslmode=disable
yaml
常见问题排查增强
问题现象 | 解决方案 | 诊断命令 |
---|---|---|
连接池耗尽 | 调整pool_size 参数 | SHOW max_connections; |
密码认证失败 | 检查pg_hba.conf 配置 | cat /etc/postgresql/*/main/pg_hba.conf |
迁移冲突 | 使用--create-only 生成迁移文件后手动解决 | npx prisma migrate dev --create-only |
时区不一致 | 设置PGTZ=Asia/Shanghai 环境变量 | SELECT current_setting('TIMEZONE'); |
外键约束失效 | 执行prisma db execute 手动修复 | SET session_replication_role = replica; (临时禁用约束) |
性能验证方案
1. 基准测试
# 安装测试工具
npm install -g pgbench
# 执行测试
pgbench -c 10 -j 2 -t 1000 -U postgres -d your_db
bash
2. 连接压力测试
// test/db-load-test.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const promises = [];
for (let i = 0; i < 100; i++) {
promises.push(prisma.user.findMany());
}
Promise.all(promises).then(() => {
console.log('Load test completed');
});
typescript
3. 索引效率分析
-- 检查索引使用情况
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'test@example.com';
-- 重建索引
REINDEX INDEX CONCURRENTLY users_email_idx;
sql
安全验证要点
1. SSL连接强制
# .env
DATABASE_URL="postgresql://user:pass@host:5432/db?sslmode=require"
dotenv
2. 权限最小化
-- 创建只读用户
CREATE ROLE readonly WITH LOGIN PASSWORD 'secret';
GRANT CONNECT ON DATABASE mydb TO readonly;
GRANT USAGE ON SCHEMA public TO readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO readonly;
sql
3. 审计日志
# postgresql.conf
log_statement = 'all'
log_connections = on
log_disconnections = on
yaml
扩展工具链
- 数据生成器:
npx prisma db seed --preview-feature
bash
- Schema比较:
npx prisma migrate diff \
--from-url $DEV_DB_URL \
--to-url $PROD_DB_URL \
--script
bash
- CI/CD集成:
# GitHub Actions示例
jobs:
db-migrate:
steps:
- run: npx prisma migrate deploy
- run: npx prisma generate
yaml
应急恢复方案
1. 数据库备份
# 逻辑备份
docker exec -t <container_id> pg_dump -U postgres -d db > backup.sql
# 物理备份
docker cp <container_id>:/var/lib/postgresql/data ./pg_backup
bash
2. 灾难恢复
# 从备份恢复
cat backup.sql | docker exec -i <container_id> psql -U postgres -d db
# 使用WAL归档
pg_basebackup -D /recovery -Ft -z -P -U replicator
bash
通过以上扩展,数据库连接验证不仅包含基础操作步骤,还提供了完整的性能优化、安全加固和灾难恢复方案,形成企业级的数据库运维体系。
知识体系拓扑详解
技术栈关联矩阵
层级 | 关键技术 | 工具链 |
---|---|---|
接口规范 | OpenAPI 3.0, JSON Schema, Protobuf | Swagger UI, Postman, Insomnia |
模块创建 | 动态模块, 全局模块, 延迟加载 | Nest CLI, Nx Monorepo |
路由定义 | 装饰器路由, 版本前缀, 文件流处理 | Fastify Adapter, Multer |
服务实现 | 领域服务, CQRS, 工作单元模式 | TypeDI, Jest Mock |
DTO验证 | 分组验证, 条件验证, 国际化消息 | class-transformer, Joi |
数据库交互 | 数据映射器, 活动记录, 读写分离 | TypeORM CLI, Prisma Studio |
跨模块协作 | 请求-响应, 发布-订阅, Saga模式 | Kafka, RabbitMQ, gRPC |
集成测试 | 容器化测试, 快照测试, 混沌工程 | Docker Compose, Pact, Chaos Mesh |
关键路径说明
- 规范到实现的闭环
- 数据库交互优化路径
扩展学习资源
- 官方文档
- 进阶教程
- 《企业级NestJS架构设计》
- 《分布式系统模式与实践》
- 工具推荐
- DB可视化: TablePlus
- API测试: Hoppscotch
常见问题解决方案
场景 | 方案 |
---|---|
循环依赖 | 使用forwardRef + 接口抽象 |
多数据库连接 | 配置多个TypeORM数据源 |
分布式事务 | 采用Saga模式 + 补偿机制 |
性能瓶颈定位 | 使用OpenTelemetry进行链路追踪 |
技术演进路线
通过以上拓扑扩展,可清晰掌握从接口定义到系统集成的完整技术链路,每个环节均包含可落地的技术选型和问题解决指南。
↑